from django.db import models

class BaseNutritionClass(models.Model):
    def resolve_relations(self, force=False):
        # By default, assume no relations exist and return true.
        return True
    
    class Meta:
        abstract = Treu

class FoodDescription(BaseNutritionClass):
    # Relation Fields
    food_group    = models.ForeignKey('FoodGroup', blank=True, null=True,
        help_text='''Related via fdgrp_cd''')
    nutrient_data = models.ForeignKey('NutrientData', blank=True, null=True,
        help_text='''Related via ndb_no''')
    weight        = models.ForeignKey('Weight', blank=True, null=True,
        help_text='''Related via ndb_no''')
    footnote      = models.ForeignKey('Footnote', blank=True, null=True,
        help_text='''Related via ndb_no''')
    
    # Imported Data Fields
    ndb_no      = models.IntegerField(max_length=10, unique=True,
        help_text='''5-digit Nutrient Databank number that uniquely identifies a food item''')
    fdgrp_cd    = models.IntegerField(max_length=8,
        help_text='''4-digit code indicating food group to which a food item belongs''')
    long_desc   = models.TextField(
        verbose_name='''long description''',
        help_text='''200-character description of food item''')
    shrt_desc   = models.CharField(max_length=255,
        verbose_name='''short description''',
        help_text='''60-character abbreviated description of food item.  Generated from the 200-character description using abbreviations in Appendix A. If short description is longer than 60 characters, additional abbreviations are made.''')
    comname     = models.CharField(max_length=255, blank=True,
        verbose_name='''common name''',
        help_text='''Other names commonly used to describe a food, including local or regional names for various foods, for example, \u201csoda\u201d or \u201cpop\u201d for \u201ccarbonated beverages\u201d''')
    manufacname = models.CharField(max_length=255, blank=True,
        verbose_name='''manufacturer name''',
        help_text='''Indicates the company that manufactured the product, when appropriate''')
    survey      = models.NullBooleanField(
        help_text='''Indicates if the food item is used in the USDA Food and Nutrient Database for Dietary Studies (FNDDS) and has a complete nutrient profile for a specified set of nutrients''')
    ref_desc    = models.TextField(blank=True,
        verbose_name='''refuse description''',
        help_text='''Description of inedible parts of a food item (refuse), such as seeds or bone''')
    refuse      = models.IntegerField(blank=True, null=True,
        help_text='''Percentage of refuse''')
    sciname     = models.CharField(max_length=255, blank=True,
        verbose_name='''scientific name''',
        help_text='''Scientific name of the food item. Given for the least processed form of the food (usually raw), if applicable''')
    n_factor    = models.FloatField(blank=True, null=True,
        help_text='''Factor for converting nitrogen to protein''')
    pro_factor  = models.FloatField(blank=True, null=True,
        help_text='''Factor for calculating calories from protein''')
    fat_factor  = models.FloatField(blank=True, null=True,
        help_text='''Factor for calculating calories from fat''')
    cho_factor  = models.FloatField(blank=True, null=True,
        help_text='''Factor for calculating calories from carbohydrate''')
    
    class Meta:
        verbose_name = "food description"
        verbose_name_plural = "food descriptions"
    
    def __unicode__(self):
        return self.long_desc
    
    def resolve_relations(self, force=False):
        # Resolve Relation Fields
        resolved = True
        if force or not self.food_group:
            try:
                self.food_group = FoodGroup.objects.get(fdgrp_cd=self.fdgrp_cd)
            except FoodGroup.DoesNotExist:
                resolved = False
        
        if force or not self.nutrient_data:
            try:
                self.nutrient_data = NutrientData.objects.get(ndb_no=self.ndb_no)
            except NutrientData.DoesNotExist:
                resolved = False
        
        if force or not self.weight:
            try:
                self.weight = Weight.objects.get(ndb_no=self.ndb_no)
            except Weight.DoesNotExist:
                resolved = False
        
        if force or not self.footnote:
            try:
                self.footnote = Footnote.objects.get(ndb_no=self.ndb_no)
            except Footnote.DoesNotExist:
                resolved = False
        self.save()
        return resolved

class FoodGroup(BaseNutritionClass):
    # Imported Data Fields
    fdgrp_cd    = models.CharField(max_length=8, unique=True,
        verbose_name='''code''',
        help_text='''4-digit code identifying a food group. Only the first 2 digits are currently assigned. In the future, the last 2 digits may be used. Codes may not be consecutive.''')
    fdgrp_desc  = models.CharField(max_length=255,
        verbose_name='''name''',
        help_text='''Name of food group''')
    
    class Meta:
        verbose_name = "food group"
        verbose_name_plural = "food groups"
    
    def __unicode__(self):
        return self.fdgrp_desc

class NutrientData(BaseNutritionClass):
    # Relation Fields
    nutrient_def    = models.ForeignKey('NutrientDefinition', blank=True, null=True,
        verbose_name='''nutrient definition''',
        help_text='''Related via nutr_no''')
    source_code     = models.ForeignKey('SourceCode', blank=True, null=True,
        help_text='''Related via src_cd''')
    data_derivation = models.ForeignKey('DataDerivation', blank=True, null=True,
        help_text='''Related via deriv_cd''')
    data_source_ln  = models.ManyToManyField('DataSourceLink', blank=True, null=True,
        verbose_name='''data source link''',
        help_text='''Related via ndb_no and nutr_no''')
    
    # Imported Data Fields
    ndb_no      = models.IntegerField(max_length=10,
        help_text='''5-digit Nutrient Databank number that uniquely identifies a food item''')
    nutr_no     = models.IntegerField(max_length=6,
        help_text='''Unique 3-digit identifier code for a nutrient''')
    nutr_val    = models.FloatField(
        help_text = '''Amount in 100 grams, edible portion''')
    num_data_pts = models.FloatField(
        help_text='''Number of data points''')
    std_error   = models.FloatField(blank=True, null=True,
        help_text='''Standard error of the mean. Null if can not be calculated''')
    src_cd      = models.IntegerField(
        help_text='''Code indicating type of data''')
    deriv_cd    = models.CharField(max_length=8, blank=True,
        help_text='''Data Derivation Code giving specific information on how the value is determined''')
    ref_ndb_no  = models.IntegerField(max_length=10, blank=True, null=True,
        help_text='''NDB number of the item used to impute a missing value.  Populated only for items added or updated starting with SR14''')
    add_nutr_mark = models.NullBooleanField(
        help_text='''Indicates a vitamin or mineral added for fortification or enrichment. This field is populated for ready-to-eat breakfast cereals and many brand-name hot cereals in food group 8''')
    num_studies = models.IntegerField(blank=True, null=True,
        help_text='''Number of studies''')
    min         = models.FloatField(blank=True, null=True,
        help_text='''Minimum value''')
    max         = models.FloatField(blank=True, null=True,
        help_text='''Maximum value''')
    df          = models.IntegerField(blank=True, null=True,
        help_text='''Degrees of freedom''')
    low_eb      = models.FloatField(blank=True, null=True,
        help_text='''Lower 95% error bound''')
    up_eb       = models.FloatField(blank=True, null=True,
        help_text='''Upper 95% error bound''')
    stat_cmt    = models.CharField(max_length=255, blank=True,
        help_text='''Statistical comments. See definitions below''')
    cc          = models.CharField(max_length=2, blank=True,
        help_text='''Confidence Code indicating data quality, based on evaluation of sample plan, sample handling, analytical method, analytical quality control, and number of samples analyzed. Not included in this release, but is planned for future releases''')
    
    class Meta:
        #unique_together = ('nutrient_def', 'source_code', 'data_derivation', 'data_source_ln')
        verbose_name = "nutrient data"
        verbose_name_plural = "nutrient data"
    
    def __unicode__(self):
        return self.nutrient_def.__unicode__()
    
    def resolve_relations(self, force=False):
        # Resolve Relation Fields
        resolved = True
        # Nutrient Definition
        if force or not self.nutrient_def:
            try:
                self.nutrient_def = NutrientDefinition.objects.get(nutr_no=self.nutr_no)
            except NutrientDefinition.DoesNotExist:
                resolved = False
        
        # Source Code
        if force or not self.source_code:
            try:
                self.source_code = SourceCode.objects.get(src_cd=self.src_cd)
            except SourceCode.DoesNotExist:
                resolved = False
        
        # Data Derivation
        if force or not self.data_derivation:
            try:
                self.data_derivation = DataDerivation.objects.get(deriv_cd=self.deriv_cd)
            except DataDerivation.DoesNotExist:
                resolved = False
        
        # Data Source Link
        if force or not self.data_source_ln:
            try:
                kwargs = { 'ndb_no': self.ndb_no, }
                if self.nutr_no:
                    kwargs['nutr_no'] = self.nutr_no
                self.data_source_ln.add(*DataSourceLink.objects.filter(**kwargs))
            except DataSourceLink.DoesNotExist:
                resolved = False
        self.save()
        return resolved

class NutrientDefinition(BaseNutritionClass):
    # Imported Data Fields
    nutr_no     = models.IntegerField(unique=True,
        help_text='''Unique 3-digit identifier code for a nutrient''')
    units       = models.CharField(max_length=14,
        help_text='''Units of measure (mg, g, ?g, and so on)''')
    tagname     = models.CharField(max_length=40, blank=True,
        help_text='''International Network of Food Data Systems (INFOODS)''')
    tagnames    = models.CharField(max_length=255,
        help_text='''A unique abbreviation for a nutrient/food component developed by INFOODS to aid in the interchange of data NutrDesc A 60 N Name of nutrient/food component''')
    num_dec     = models.IntegerField(
        verbose_name='''number of decimal places''',
        help_text='''Number of decimal places to which a nutrient value is rounded''')
    sr_order    = models.IntegerField(
        verbose_name='''sorting order''',
        help_text='''Used to sort nutrient records in the same order as various reports produced from SR''')
    
    class Meta:
        verbose_name = "nutrient definition"
        verbose_name_plural = "nutrient definitions"
    
    def __unicode__(self):
        return u"%s" % self.tagnames

class SourceCode(BaseNutritionClass):
    # Imported Data Fields
    src_cd      = models.IntegerField(unique=True,
        verbose_name='''code''',
        help_text='''2-digit code''')
    srccd_desc  = models.CharField(max_length=255,
        verbose_name='''description''',
        help_text='''Description of source code that identifies the type of nutrient data''')
    
    class Meta:
        verbose_name = "source code"
        verbose_name_plural = "source codes"
    
    def __unicode__(self):
        return u"%s" % self.srccd_desc

class DataDerivation(BaseNutritionClass):
    # Imported Data Fields
    deriv_cd    = models.CharField(max_length=8, unique=True,
        help_text='''Derivation Code''')
    deriv_desc  = models.TextField(
        help_text='''Description of derivation code giving specific information 
    on how the value was determined''')
    
    class Meta:
        verbose_name = "data derivation"
        verbose_name_plural = "data derivations"
    
    def __unicode__(self):
        return self.deriv_cd

class Weight(BaseNutritionClass):
    # Imported Data Fields
    ndb_no      = models.IntegerField(
        help_text='''5-digit Nutrient Databank number''')
    seq         = models.IntegerField(
        help_text='''Sequence number''')
    amount      = models.FloatField(
        help_text='''Unit modifier (for example, 1 in \u201c1 cup\u201d)''')
    msre_desc   = models.TextField(
        help_text='''Description (for example, cup, diced, and 1-inch pieces)''')
    gm_wgt      = models.FloatField(
        help_text='''Gram weight''')
    num_data_pts = models.FloatField(blank=True, null=True,
        help_text='''Number of data points''')
    std_dev     = models.FloatField(blank=True, null=True,
        help_text='''Standard deviation''')
    
    class Meta:
        unique_together = ('ndb_no', 'seq')
        verbose_name = "weight"
        verbose_name_plural = "weights"
    
    def __unicode__(self):
        return u"%s - %s" % (self.ndb_no, self.msre_desc)


FOOTNOTE_TYPE_CHOICES = (
    ('D', 'food description'),
    ('M', 'measure description'),
    ('N', 'nutrient value'),
)

class Footnote(BaseNutritionClass):
    # Relation Fields
    nutrient_def    = models.ForeignKey('NutrientDefinition', blank=True, null=True,
        help_text='''Related via nutr_no''')
    
    # Imported Data Fields
    ndb_no      = models.IntegerField(
        help_text='''5-digit Nutrient Databank number''')
    footnt_no   = models.IntegerField(blank=True, null=True,
        help_text='''Sequence number. If a given footnote applies to more than one nutrient number, the same footnote number is used. As a result, this file cannot be indexed''')
    footnt_typ  = models.CharField(max_length=1, choices=FOOTNOTE_TYPE_CHOICES,
        help_text='''Type of footnote: 
            D = footnote adding information to the food  description;  
            M = footnote adding information to measure description;  
            N = footnote providing additional information on a nutrient value. If the Footnt_typ = N, the Nutr_No will also be filled in''')
    nutr_no     = models.IntegerField(blank=True, null=True,
        help_text='''Unique 3-digit identifier code for a nutrient to which footnote applies''')
    footnt_txt  = models.TextField(
        help_text='''Footnote text''')
    
    class Meta:
        unique_together = ('ndb_no', 'footnt_no', 'nutr_no')
        verbose_name = "footnote"
        verbose_name_plural = "footnotes"
    
    def __unicode__(self):
        return "%s - %s" % (self.ndb_no, self.footnt_no)
    
    def resolve_relations(self, force=False):
        # Resolve Relation Fields
        resolved = True
        if force or not self.nutrient_def:
            try:
                self.nutrient_def = NutrientData.objects.get(nutr_no=self.nutr_no)
            except NutrientData.DoesNotExist:
                resolved = False
            self.save()
        return resolved

class DataSourceLink(BaseNutritionClass):
    # Relation Fields
    data_source    = models.ForeignKey('DataSource', blank=True, null=True,
        help_text='''Related via datasrc_id''')
    
    # Imported Data Fields
    ndb_no      = models.IntegerField(
        help_text='''5-digit Nutrient Databank number''')
    nutr_no     = models.IntegerField(
        help_text='''Unique 3-digit identifier code for a nutrient''')
    datasrc_id  = models.CharField(max_length=12,
        help_text='''Unique ID identifying the reference/source''')
    
    class Meta:
        verbose_name = "data source link"
        verbose_name_plural = "data source links"
    
    def __unicode__(self):
        return u"%s - %s" % (self.ndb_no, self.nutr_no)
    
    def resolve_relations(self, force=False):
        # Resolve Relation Fields
        resolved = True
        if force or not self.data_source:
            try:
                self.data_source = DataSource.objects.get(datasrc_id=self.datasrc_id)
            except DataSource.DoesNotExist:
                resolved = False
            self.save()
        return resolved

class DataSource(BaseNutritionClass):
    # Imported Data Fields
    datasrc_id  = models.CharField(max_length=10, unique=True,
        help_text='''Unique number identifying the reference/source''')
    authors     = models.CharField(max_length=255, blank=True, null=True,
        help_text='''List of authors for a journal article or name of sponsoring  organization for other documents''')
    title       = models.CharField(max_length=255, blank=True,
        help_text='''Title of article or name of document, such as a report from  a company or trade association''')
    year        = models.IntegerField(max_length=8, blank=True, null=True,
        help_text='''Year article or document was published''')
    journal     = models.CharField(max_length=255, blank=True,
        help_text='''Name of the journal in which the article was published''')
    vol_city    = models.CharField(max_length=255, blank=True,
        help_text='''Volume number for journal articles, books, or reports; city where sponsoring organization is located''')
    issue_state = models.CharField(max_length=10, blank=True,
        help_text='''Issue number for journal article; State where the sponsoring organization is located''')
    start_page  = models.CharField(max_length=10, blank=True,
        help_text='''Starting page number of article/document''')
    end_page    = models.CharField(max_length=10, blank=True,
        help_text='''Ending page number of article/document''')
    
    class Meta:
        verbose_name = "data source"
        verbose_name_plural = "data sources"
    
    def __unicode__(self):
        return self.title